package com.gpj.sqlbuilder.converters;

import com.gpj.sqlbuilder.annotations.ExcelDynamicOrder;
import com.gpj.sqlbuilder.annotations.ExcelOrder;
import com.gpj.sqlbuilder.enums.ColumnType;
import com.gpj.sqlbuilder.enums.PrimaryType;
import com.gpj.sqlbuilder.store.excel.CellInfo;
import com.gpj.sqlbuilder.store.excel.RowInfo;
import com.gpj.sqlbuilder.store.excel.Sheet;
import com.gpj.sqlbuilder.store.sql.Column;
import com.gpj.sqlbuilder.store.sql.Data;
import com.gpj.sqlbuilder.store.sql.Index;
import com.gpj.sqlbuilder.store.sql.Table;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * @className Resolver
 * @description
 *   TODO
 * @author GPJ
 * @date 2021/3/26 15:20
 * @version 1.0
 **/
@Slf4j
public class ExcelConverter {
	private static final String SHEET_NAME_TABLE = "table";
	private static final String SHEET_NAME_COLUMN = "column";
	private static final String SHEET_NAME_INDEX = "index";
	private static final String SHEET_NAME_DATAS = "datas";

	private Map<String, Table> tables = new HashMap<>();
	private List<String> order = new ArrayList<>();

	/**
	 * 解析内存中的Excel数据,生成建表数据
	 * @param sheetMap Excel缓存数据
	 * @return
	 */
	public List<Table> convert(Map<String, Sheet> sheetMap){
		tables.clear();
		try {
			log.info("-- 生成数据 开始 --");
			boolean result = analysisForTableInfo(sheetMap.get(SHEET_NAME_TABLE))
					&& analysisForColumnInfo(sheetMap.get(SHEET_NAME_COLUMN))
					&& analysisForIndexInfo(sheetMap.get(SHEET_NAME_INDEX))
					&& analysisForDatasInfo(sheetMap.get(SHEET_NAME_DATAS));
			if(result){
				log.info("\t数据生成 成功");
			}else{
				log.error("\t数据生成 失败");
			}
		} catch (Exception e) {
			log.error("\t数据生成 错误:{}", e.toString());
			if(log.isDebugEnabled()){
				log.error(e.getMessage(), e);
			}
		}finally {
			log.info("-- 生成数据 结束 --");
		}
		return order.stream().map(tables::get).collect(Collectors.toList());
	}

	/**
	 * 解析表信息
	 * @param sheet
	 */
	private boolean analysisForTableInfo(Sheet sheet) throws Exception {
		log.info("\t解析表信息");
		return analysisDefault(sheet, Table.class, table -> {
			tables.put(table.getName(), table);
			order.add(table.getName());
		});
	}

	/**
	 * 解析字段信息
	 * @param sheet
	 */
	private boolean analysisForColumnInfo(Sheet sheet) throws Exception {
		log.info("\t解析字段信息");
		return analysisDefault(sheet, Column.class, column -> {
			List<Column> columns = tables.get(column.getTableName()).getColumns();
			columns.add(column);
		});
	}

	/**
	 * 解析索引信息
	 * @param sheet
	 */
	private boolean analysisForIndexInfo(Sheet sheet) throws Exception {
		log.info("\t解析索引信息");
		return analysisDefault(sheet, Index.class, index -> {
			tables.get(index.getTableName()).getIndexs().put(index.getIndexName(), index);
		}, (index, cellInfo) -> tables.get(index.getTableName()).getColumn(cellInfo.getValue()) );
	}

	/**
	 * 解析初始化数据
	 * @param sheet
	 */
	private boolean analysisForDatasInfo(Sheet sheet) throws Exception {
		log.info("\t解析初始化数据");
		return analysisDefault(sheet, Data.class, data -> {
			tables.get(data.getTableName()).getDatas().add(data);
		}, (data, cellInfo) -> cellInfo.getValue() );
	}

	/**
	 * 根据对象字段的注解自动填入对应单元格数据,并生成对象
	 * @param cells 当前行单元格对象集合
	 * @param clazz 要生成对象类型
	 */
	private <T> T defaultBuild(List<CellInfo> cells, Class<T> clazz) throws Exception {
		Field[] fields = clazz.getDeclaredFields();

		T instance = clazz.newInstance();

		for (Field field : fields) {
			if(field.isAnnotationPresent(ExcelOrder.class)){
				ExcelOrder annotation = field.getAnnotation(ExcelOrder.class);
				int index = annotation.value();
				CellInfo info = cells.get(index);
				if(StringUtils.isNotBlank(info.getValue())){
					field.setAccessible(true);
					setValueToField(field, instance, info.getValue());
				}else if(annotation.required()){
					log.error("\t\t数据缺失：[sheet:{},row:{},cell:{}]", info.getSheetName(), info.getRowNum(), info.getIndex());
				}
			}
		}
		return instance;
	}

	/**
	 * 逐行解析Excel数据
	 * @param sheet sheet对象
	 * @param clazz 生成对象的类型
	 * @param consumer 对象生成后的处理函数
	 */
	private <T> boolean analysisDefault(Sheet sheet, Class<T> clazz, Consumer<T> consumer) throws Exception {
		return analysisDefault(sheet, clazz, consumer, null);
	}

	/**
	 * 逐行解析Excel数据
	 * @param sheet sheet对象
	 * @param clazz 生成对象的类型
	 * @param consumer 对象生成后的处理函数
	 * @param handle 动态列字段的处理函数
	 */
	private <T,R> boolean analysisDefault(Sheet sheet, Class<T> clazz, Consumer<T> consumer, AnalysisHandle<T, CellInfo,R> handle) throws Exception {
		if(sheet == null) return false;
		for (RowInfo row : sheet.getRows()) {
			T instance = defaultBuild(row.getCells(), clazz);

			if(instance == null){
				log.error("\t\t解析[sheet:{},row:{}] [解析失败]", row.getSheetName(), row.getRowNum());
				return false;
			}


			if(handle != null){
				List<CellInfo> cells = row.getCells();
				for (Field field : clazz.getDeclaredFields()) {
					if(field.isAnnotationPresent(ExcelDynamicOrder.class)){
						ExcelDynamicOrder annotation = field.getAnnotation(ExcelDynamicOrder.class);

						List<R> list = new ArrayList<>();
						for (int i = annotation.start(); i < Math.min(cells.size(), annotation.end()); i++) {
							R item = handle.analysis(instance, cells.get(i));
							if(item != null){
								list.add(item);
							}
						}
						field.setAccessible(true);
						field.set(instance, list);
					}
				}
			}
			consumer.accept(instance);
			log.debug("\t\t解析[sheet:{},row:{}] [解析成功]", row.getSheetName(), row.getRowNum());
		}
		return true;
	}

	/**
	 * 反射设置字段数据
	 * @param field 字段对象
	 * @param obj 实例对象
	 * @param value 字段值
	 */
	private void setValueToField(Field field, Object obj, String value) throws Exception {
		field.setAccessible(true);
		if(field.getType() == int.class
				|| field.getType() == Integer.class){// int
			field.set(obj, Integer.valueOf(value));
		}else if(field.getType() == short.class
				|| field.getType() == Short.class){// short
			field.set(obj, Short.valueOf(value));
		}else if(field.getType() == long.class
				|| field.getType() == Long.class){// long
			field.set(obj, Long.valueOf(value));
		}else if(field.getType() == float.class
				|| field.getType() == Float.class){// float
			field.set(obj, Float.valueOf(value));
		}else if(field.getType() == double.class
				|| field.getType() == Double.class){// double
			field.set(obj, Double.valueOf(value));
		}else if(field.getType() == boolean.class
				|| field.getType() == Boolean.class){// boolean
			ExcelOrder annotation = field.getAnnotation(ExcelOrder.class);
			String match = annotation.match();
			field.set(obj, StringUtils.isBlank(match)?StringUtils.isNotBlank(value):StringUtils.equals(match, value));
		}else if(field.getType() == String.class ){// String
			field.set(obj, value);
		}else if(field.getType() == List.class ){// list
			field.set(obj, value);
		}else if(field.getType() == ColumnType.class ){// 字典--ColumnType
			ColumnType type = ColumnType.getColumnType(value);
			field.set(obj, type);
		}else if(field.getType() == PrimaryType.class ){// 字典--PrimaryType
			PrimaryType type = PrimaryType.getPrimaryType(value);
			field.set(obj, type);
		}else {
			throw new RuntimeException("无效的field类型");
		}
	}
}
